package com.jichangxiu.system.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Dynamic;
import com.jichangxiu.common.annotation.Scope;
import com.jichangxiu.common.entity.bo.JcxContextEntity;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.common.entity.rule.LogRule;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.DynamicUtils;
import com.jichangxiu.framework.utils.NoUtils;
import com.jichangxiu.framework.utils.SecurityUtils;
import com.jichangxiu.system.entity.bo.SysLogBo;
import com.jichangxiu.system.entity.dto.edit.EditSysLog;
import com.jichangxiu.system.entity.dto.query.QuerySysLog;
import com.jichangxiu.system.entity.po.SysLog;
import com.jichangxiu.system.entity.po.SysUser;
import com.jichangxiu.system.entity.vo.SysLogVo;
import com.jichangxiu.system.mapper.SysLogMapper;
import com.jichangxiu.system.service.SysLogService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotBlank;
import java.util.List;

/**
 * SysLogServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:26
 */
@Service
public class SysLogServiceImpl extends BaseServiceImpl<SysLogMapper, SysLog, SysLogVo> implements SysLogService {

    /**
     * 保存操作日志
     *
     * @param logRule 操作日志载体
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveLogRule(JcxContextEntity jcxContextEntity, LogRule logRule) {
        DynamicUtils.changeSource(jcxContextEntity.getTenantSource());
        SysLog sysLog = SysLog.builder()
                .id(IdWorker.getIdStr())
                .no(NoUtils.createNo(jcxContextEntity.getTenantId(), "log"))
                .tenantId(logRule.getTenantId())
                .userId(logRule.getUserId())
                .type(logRule.getType())
                .action(logRule.getAction())
                .method(logRule.getMethod())
                .module(logRule.getModule())
                .handle(logRule.getHandle())
                .param(logRule.getParam())
                .result(logRule.getResult())
                .exception(logRule.getException())
                .address(logRule.getAddress())
                .location(logRule.getLocation())
                .browser(logRule.getBrowser())
                .system(logRule.getSystem())
                .status(logRule.getStatus())
                .duration(logRule.getDuration())
                .remark(logRule.getRemark())
                .build();
        sysLog.setDeptBy(jcxContextEntity.getDeptId());
        sysLog.setCreateBy(jcxContextEntity.getUserId());
        sysLog.setUpdateBy(jcxContextEntity.getUserId());
        getBaseMapper().insert(sysLog);
        DynamicUtils.resetSource();
    }

    /**
     * 修改系统日志
     *
     * @param editSysLog 修改系统日志视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysLog editSysLog) {
        return getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysLog, SysLogBo.class))) > 0;
    }

    /**
     * 根据主键查询系统日志
     *
     * @param id       系统日志主键
     * @param isExtend 是否扩展
     * @return 系统日志详情
     */
    @Dynamic
    @Override
    public SysLogVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysLogVo sysLogVo = getBaseMapper().selectJoinOne(SysLogVo.class, buildQueryWrapper(QuerySysLog.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysLogVo))
            sysLogVo = renderSysLogVoList(ListUtil.toList(sysLogVo)).get(0);
        return sysLogVo;
    }

    /**
     * （可分页）查询系统日志列表
     *
     * @param querySysLog 查询视图对象
     * @return （分页）系统日志列表
     */
    @Scope
    @Dynamic
    @Override
    public Page<SysLogVo> lists(QuerySysLog querySysLog) {
        Page<SysLogVo> sysLogVoPage = getBaseMapper().selectJoinPage(startPage(), SysLogVo.class, buildQueryWrapper(querySysLog));
        if (Boolean.TRUE.equals(querySysLog.getIsExtend()))
            sysLogVoPage.setRecords(renderSysLogVoList(sysLogVoPage.getRecords()));
        return sysLogVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysLogBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysLog addAndEditValidBo(SysLogBo sysLogBo) {
        SysLog sysLog = ConvertUtils.convert(sysLogBo, SysLog.class);
        if (ObjectUtil.isNull(sysLog))
            throw new ServiceException("待校验对象为空");
        if (StrUtil.isEmpty(sysLog.getId())) {
            // 新增
            sysLog.setTenantId(SecurityUtils.getTenantId());
            return sysLog;
        } else {
            // 修改
            SysLog dbSysLog = getById(sysLog.getId());
            dbSysLog.setRemark(sysLog.getRemark());
            return dbSysLog;
        }
    }

    /**
     * 渲染视图
     *
     * @param sysLogVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysLogVo> renderSysLogVoList(List<SysLogVo> sysLogVoList) {
        if (ObjectUtil.isNotEmpty(sysLogVoList)) {
            // todo 扩展信息填入
        }
        return sysLogVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysLog 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysLog> buildQueryWrapper(QuerySysLog querySysLog) {
        MPJLambdaWrapper<SysLog> lambdaQueryWrapper = new MPJLambdaWrapper<SysLog>()
                .selectAll(SysLog.class)
                .leftJoin(SysUser.class, SysUser::getId, SysLog::getUserId, ext -> ext
                        .selectAs(SysUser::getName, SysLogVo::getUserName))
                .disableSubLogicDel();
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getId()), SysLog::getId, querySysLog.getId());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getNo()), SysLog::getNo, querySysLog.getNo());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getType()), SysLog::getType, querySysLog.getType());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getAction()), SysLog::getAction, querySysLog.getAction());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getMethod()), SysLog::getMethod, querySysLog.getMethod());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getModule()), SysLog::getModule, querySysLog.getModule());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getHandle()), SysLog::getHandle, querySysLog.getHandle());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getParam()), SysLog::getParam, querySysLog.getParam());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getResult()), SysLog::getResult, querySysLog.getResult());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getException()), SysLog::getException, querySysLog.getException());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getAddress()), SysLog::getAddress, querySysLog.getAddress());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getLocation()), SysLog::getLocation, querySysLog.getLocation());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getBrowser()), SysLog::getBrowser, querySysLog.getBrowser());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getSystem()), SysLog::getSystem, querySysLog.getSystem());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysLog.getStatus()), SysLog::getStatus, querySysLog.getStatus());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysLog.getDuration()), SysLog::getDuration, querySysLog.getDuration());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysLog.getUserId()), SysLog::getUserId, querySysLog.getUserId());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysLog.getRemark()), SysLog::getRemark, querySysLog.getRemark());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysLog.getScope()), querySysLog.getScope());
        return lambdaQueryWrapper;
    }

}